package optimizers.ganeatgeneric;

import java.awt.GridLayout;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import java.util.concurrent.ExecutorCompletionService;

import java.util.concurrent.Executors;

import javax.swing.JFrame;
import javax.swing.JPanel;

import org.math.plot.utils.Array;

import optimizers.commons.GAConstants;
import optimizers.commons.GAIndiv;
import optimizers.commons.GenericGA;
import optimizers.commons.Logger;

import optimizers.commons.RunConfig;
import optimizers.commons.evaluator.FitnessEvaluationService;


import optimizers.ganeat.Constants;

import optimizers.ganeatgeneric.report.LoggerNEAT;

import util.MyUtil;
import visualization.ganeatgeneric.GraphDisplayer;
import visualization.PlotExpData;
import visualization.ganeatgeneric.PlotSpeciesData;

public abstract class GA extends GenericGA{

	public Map<String, Integer> innovations;
	public Map<String, String> nodeNames;
	ArrayList<Individual> speciesLib;
	double[][] speciesData = new double[2][Constants.generations + 1];
	ArrayList<ArrayList<Species>> speciesVisualData = new ArrayList<ArrayList<Species>>();
	GraphDisplayer bestIndividuals = new GraphDisplayer("Best Individual for each generations");
	JFrame graphFrame = new JFrame("Metrics over generations");
	JPanel panel = new JPanel();
	JFrame graphFrame1 = new JFrame("Species Visualization");
	JPanel panel1 = new JPanel();

	public GA(RunConfig config) {
		super(config);
		innovations = new HashMap<String, Integer>();
		nodeNames = new HashMap<String, String>();
		speciesLib = new ArrayList<Individual>();
	}

	//TODO: check that this doesn't cause conflicts with the higher level function with the same name
	public abstract Individual getInitialIndividual();

	public abstract Individual getNewIndividual(int nbOfSimpleSequences, Map<String, Integer> innovations2,
			Map<String, String> nodeNames2,
			int i, int j);

	@Override
	protected Logger startLogging(){
		DateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd HH_mm_ss");
		Date date = new Date();
		String fileName = "result/NEAT_" + getProblemName() + "_"
			+ dateFormat.format(date);
		return new LoggerNEAT(fileName,true, speciesVisualData,
				speciesData, innovations, nodeNames, speciesLib, config);
	}
	
	@Override
	protected void initializePopulation(GAIndiv[] population){
		
		Individual indiv = getInitialIndividual();
		Individual newSpecies = null;
		try {
			newSpecies = clone(indiv);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		speciesLib.add(newSpecies);
		
		if (!Constants.SILENT_MODE && config.evaluator != null) {
			indiv.fitnessResult = config.evaluator.evaluateFitness(indiv
					.getOligoSystem());
			bestIndividuals.addGraph(
					indiv.getOligoSystem().getVisualization(250, 250),
					indiv.displayTimeSereis(), indiv.printModelInTextArea());
			bestIndividuals.display();
		}
		
		for (int i = 0; i < Constants.popSize; i++) {
			try {
				population[i] = clone(indiv);
			} catch (CloneNotSupportedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			((Individual)population[i]).generationIndex = 0;
			((Individual)population[i]).individualIndex = i;
			((Individual)population[i]).mutate();
		}
		
		int count = 0;
		for (int index = 0; index < Constants.popSize; index++) {
			if (((Individual)population[index]).mutationInfo.contains("(-)")) {
				count++;
			}
		}
		System.out.println("Switch mutation happened " + count + " times.");
	}
	
	@Override
	protected void initAlgorithm(){
		
		graphFrame.setSize(1200, 800);
		GridLayout grid = new GridLayout(0, 1);
		
		panel.setLayout(grid);
		grid.setColumns(2);
		graphFrame.add(panel);

		
		graphFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		int speciesVisualW = 1500;
		int speciesVisualH = 800;
		graphFrame1.setSize(speciesVisualW, speciesVisualH);
		GridLayout grid1 = new GridLayout(0, 1);
		panel1.setLayout(grid1);
		grid1.setColumns(1);
		graphFrame1.add(panel1);
	}
	
	@Override
	protected void evolvePopulation(GAIndiv[] nextGen){
		int i = 0;
		int generation = speciesVisualData.size()-1;
		ArrayList<Species> thisGenSpecies = speciesVisualData
		.get(generation); //get last species added
		for (Species sp : thisGenSpecies) {
			int nextGenPop = sp.getNextGenPopulation();
			if (sp.getNextGenPopulation() > 0) {
				try {
					nextGen[i] = clone(sp.getBestIndividual());
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
				((Individual)nextGen[i]).generationIndex = generation;
				((Individual)nextGen[i]).individualIndex = i;
				i++;
				nextGenPop--;
			}
			int nToRemove = Math.min(sp.individuals.size() / 2,
					sp.individuals.size() - 2);
			System.out.println("Removing " + nToRemove);
			for (int j = 0; j < nToRemove; j++) {
				Individual indivToRemove = sp.individuals.get(0);
				for (Individual indiv1 : sp.individuals) {
					if (indivToRemove.getFitness() > indiv1
							.getFitness()) {
						indivToRemove = indiv1;
					}
				}
				sp.individuals.remove(indivToRemove);
			}
			for (int j = 0; j < nextGenPop; j++) {
//				if (rand.nextDouble() < Constants.probMutationOnly) {
//					Individual[] parents = selectCrossoverParents(sp.individuals);
//					try {
//						nextGen[i] = clone(parents[0]);
//					} catch (CloneNotSupportedException e) {
//						e.printStackTrace();
//					}
//					((Individual)nextGen[i]).mutate();
//					((Individual)nextGen[i]).generationIndex = generation;
//					((Individual)nextGen[i]).individualIndex = i;
//					((Individual)nextGen[i]).parent1 = parents[0];
//					((Individual)nextGen[i]).parent2 = parents[0];
//				} else {
//					if (rand.nextDouble() < Constants.interspeciesMatingRate) {
//						System.out.println("b");
//						Species interSpecies = null;
//						do {
//							interSpecies = thisGenSpecies.get(rand
//									.nextInt(thisGenSpecies.size()));
//						} while (interSpecies != sp);
//						Individual[] parents1 = selectCrossoverParents(sp.individuals);
//						Individual[] parents2 = selectCrossoverParents(interSpecies.individuals);
//						try {
//							nextGen[i]= crossOver(parents1[0], parents2[0]);
//						} catch (CloneNotSupportedException e) {
//							e.printStackTrace();
//						}
//						((Individual)nextGen[i]).generationIndex = generation;
//						((Individual)nextGen[i]).individualIndex = i;
//						((Individual)nextGen[i]).parent1 = parents1[0];
//						((Individual)nextGen[i]).parent2 = parents2[0];
//						if (this.rand.nextDouble() < Constants.probIndividualMutation) {
//							((Individual)nextGen[i]).mutate();
//						}
//					} else {
//						Individual[] parents = selectCrossoverParents(sp.individuals);
//						try {
//							nextGen[i] = crossOver(parents[0], parents[1]);
//						} catch (CloneNotSupportedException e) {
//							e.printStackTrace();
//						}
//						((Individual)nextGen[i]).generationIndex = generation;
//						((Individual)nextGen[i]).individualIndex = i;
//						((Individual)nextGen[i]).parent1 = parents[0];
//						((Individual)nextGen[i]).parent2 = parents[1];
//						if (this.rand.nextDouble() < Constants.probIndividualMutation) {
//							((Individual)nextGen[i]).mutate();
//						}
//					}
//				}
				if (rand.nextDouble() < Constants.probMutationOnly) {
					Individual[] parents = selectCrossoverParents(sp.individuals);
					nextGen[i] = clone(parents[0]);
					nextGen[i].mutate();
					((Individual)nextGen[i]).generationIndex = generation;
					((Individual)nextGen[i]).individualIndex = i;
					((Individual)nextGen[i]).parent1 = parents[0];
					((Individual)nextGen[i]).parent2 = parents[0];
				} else {
					if (rand.nextDouble() < Constants.interspeciesMatingRate) {
						System.out.println("b");
						Species interSpecies = null;
						do {
							interSpecies = thisGenSpecies.get(rand
									.nextInt(thisGenSpecies.size()));
						} while (interSpecies != sp);
						Individual[] parents1 = selectCrossoverParents(sp.individuals);
						Individual[] parents2 = selectCrossoverParents(interSpecies.individuals);
						nextGen[i] = crossOver1Point(parents1[0], parents2[0]);
						((Individual)nextGen[i]).generationIndex = generation;
						((Individual)nextGen[i]).individualIndex = i;
						((Individual)nextGen[i]).parent1 = parents1[0];
						((Individual)nextGen[i]).parent2 = parents2[0];
						if (this.rand.nextDouble() < Constants.probIndividualMutation) {
							nextGen[i].mutate();
						}
					} else {
						Individual[] parents = selectCrossoverParents(sp.individuals);
						nextGen[i] = crossOver1Point(parents[0], parents[1]);
						((Individual)nextGen[i]).generationIndex = generation;
						((Individual)nextGen[i]).individualIndex = i;
						((Individual)nextGen[i]).parent1 = parents[0];
						((Individual)nextGen[i]).parent2 = parents[1];
						if (this.rand.nextDouble() < Constants.probIndividualMutation) {
							nextGen[i].mutate();
						}
					}
				}
				
				i++;
			}
		}
		System.out.println("Done removing");

		// fill the rest with mutation only
		while (i < Constants.popSize) {
			Species randomSpecies = thisGenSpecies.get(rand
					.nextInt(thisGenSpecies.size()));
			Individual parents = randomSpecies.individuals.get(rand
					.nextInt(randomSpecies.individuals.size()));
			try {
				nextGen[i] = clone(parents);
			} catch (CloneNotSupportedException e) {
				
				e.printStackTrace();
			}
			((Individual)nextGen[i]).mutate();
			((Individual)nextGen[i]).generationIndex = generation;
			((Individual)nextGen[i]).individualIndex = i;
			((Individual)nextGen[i]).parent1 = parents;
			((Individual)nextGen[i]).parent2 = parents;
			i++;
		}
	
		int count = 0;
		for (int index = 0; index < Constants.popSize; index++) {
			if (((Individual)nextGen[index]).mutationInfo.contains("(-)")) {
				count++;
			}
		}
		System.out.println("Switch mutation happened " + count + " times.");
		
	}
	
	@Override
	protected void postEvaluation(GAIndiv[] nextGen){
		
	}
	
	public void run() {
		
		logger = startLogging();
		
		Individual[] nextGen = new Individual[Constants.popSize];

		eservice = Executors.newFixedThreadPool(Constants.nParallelThreads);
		cservice = new ExecutorCompletionService<Object>(eservice);

		evaluationService = new FitnessEvaluationService(cservice,
				config.evaluator, maxRunTime);
		
		// System.out.println("Displaying 1st individual");
		
		initAlgorithm();

		// generation loop
		for (int generation = 0; generation <= Constants.generations; generation++) {
			//logger.logToMemory("Generation " + generation);
			if (generation == 0) { // initial generation
				initializePopulation(nextGen);
			} else { // from 1st generation
				
				evolvePopulation(nextGen);
			}

			
			// Evaluate fitness in parallel

			// Launch all evaluations. Separate function, so that it can be
			// easily overridden by children.
			launchEvaluation(nextGen, logger);
			
			System.out.println("Error Individuals ");
			for (int i = 0; i < nextGen.length; i++) {
				if (nextGen[i].fitnessResult == null) {
					System.out.println(nextGen[i].toString());
					System.out.println("-------------------");
				}
			}

			postEvaluation(nextGen);
		}
	}

	protected void postSpeciation(ArrayList<Species> nextGenSpecies) {
		// TODO Auto-generated method stub

	}

	protected ArrayList<Species> speciation(Individual[] nextGen){
		int generation = speciesVisualData.size()-1;
		for (int i = 0; i < nextGen.length; i++) {
			// compare with previous species' best individuals
			if (generation > 0) {
				double curSpeciesFitness = 0;
				for (Species sp : speciesVisualData.get(generation - 1)) {
					Individual bestIndiv = sp.getBestIndividual();
					if (curSpeciesFitness < bestIndiv.getFitness()) {
						if (compDistance(nextGen[i], bestIndiv) < Constants.speciationThreshold) {
							nextGen[i].spicies = sp.representative;
							nextGen[i].indivCompared = bestIndiv;
							curSpeciesFitness = bestIndiv.getFitness();
						}
					}
				}
			}

			// compare with every individual in previous generation
			if (nextGen[i].spicies == null && generation > 0) {
				for (Species sp : speciesVisualData.get(generation - 1)) {
					for (Individual indiv1 : sp.individuals) {
						if (compDistance(nextGen[i], indiv1) < Constants.speciationThreshold) {
							nextGen[i].spicies = sp.representative;
							nextGen[i].indivCompared = indiv1;
							break;
						}
					}
				}
			}

			// compare with history species
			if (nextGen[i].spicies == null) {
				for (Individual indiv1 : speciesLib) {
					if (compDistance(nextGen[i], indiv1) < Constants.speciationThreshold) {
						nextGen[i].spicies = indiv1;
						nextGen[i].indivCompared = indiv1;
						break;
					}
				}
			}

			Individual newSpecies = null;
			// make new species
			if (nextGen[i].spicies == null) {
				try {
					newSpecies = clone(nextGen[i]);
				} catch (CloneNotSupportedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				speciesLib.add(newSpecies);
				nextGen[i].spicies = newSpecies;
				nextGen[i].indivCompared = null;
				System.out.println("-------->Species added: "
						+ newSpecies.generationIndex + ","
						+ newSpecies.individualIndex);
			}

		}

		ArrayList<Species> nextGenSpecies = new ArrayList<Species>();
		for (Individual species : speciesLib) {
			Species sp = new Species(species);
			/*
			 * if (generation > 0) { for (Species sp1 :
			 * speciesVisualData.get(generation - 1)) { if
			 * (sp1.representative == sp.representative) { for (Individual
			 * indiv1 : sp1.individuals) { sp.individuals.add(indiv1); } } }
			 * }
			 */
			for (int i = 0; i < nextGen.length; i++) {
				if (nextGen[i].spicies == sp.representative) {
					sp.individuals.add(nextGen[i]);
				}
			}
			if (sp.individuals.size() > 0) {
				nextGenSpecies.add(sp);
			}
		}
		speciesVisualData.add(nextGenSpecies);

		// calculate next gen population for each species
		ArrayList<Species> cappedSpecies = new ArrayList<Species>();
		int popSizeLeft = Constants.popSize;
		// calculate 1st time
		double sumFitness = 0;
		for (Species sp : nextGenSpecies) {
			sumFitness += sp.getSpeciesFitness();
		}
		for (Species sp : nextGenSpecies) {
			int nextGenPop = (int) (sp.getSpeciesFitness() * popSizeLeft / sumFitness);
			// capping
			if (nextGenPop > sp.individuals.size() + Constants.popSize / 10) {
				nextGenPop = sp.individuals.size() + Constants.popSize / 10;
				sp.setNextGenPopulation(nextGenPop);
				cappedSpecies.add(sp);
				popSizeLeft -= nextGenPop;
			}
		}

		// calculate 2nd time removing the capped species
		sumFitness = 0;
		for (Species sp : nextGenSpecies) {
			if (!cappedSpecies.contains(sp)) {
				sumFitness += sp.getSpeciesFitness();
			}
		}
		for (Species sp : nextGenSpecies) {
			if (!cappedSpecies.contains(sp)) {
				int nextGenPop = (int) (sp.getSpeciesFitness()
						* popSizeLeft / sumFitness);
				sp.setNextGenPopulation(nextGenPop);
			}
		}
		return nextGenSpecies;
	}
	
	protected void postEvaluation(Individual[] nextGen) {
		double[] fitness = new double[Constants.popSize];
		int bestIndividualIndex = 0;
		int generation = speciesVisualData.size(); //since it hasn't been updated yet, doesn't need to do "-1"
		for (int i = 1; i < nextGen.length; i++) {
			if (nextGen[bestIndividualIndex].getFitness() < nextGen[i]
					.getFitness()) {
				bestIndividualIndex = i;
			}
		}

		if (!Constants.SILENT_MODE) {
			updateBestIndividuals(bestIndividuals,
					nextGen[bestIndividualIndex]);

		}

		// Speciation
		System.out.println("Start speciation");
		ArrayList<Species> nextGenSpecies = speciation(nextGen);
		System.out.println("Done speciation");

		postSpeciation(nextGenSpecies);

		// modify the speciationThreshold
		if (Constants.autoSpeciationThreshold) {
			int nSpecies = nextGenSpecies.size();
			if (nSpecies < Constants.targetNSpecies) {
				Constants.speciationThreshold -= Constants.speciationThresholdMod;
			} else if (nSpecies > Constants.targetNSpecies) {
				Constants.speciationThreshold += Constants.speciationThresholdMod;
			}
			Constants.speciationThreshold = Math.max(
					Constants.speciationThreshold,
					Constants.speciationThresholdMin);
		}

		// calculate fitness including sharing
		for (int i = 0; i < nextGen.length; i++) {
			for (Species sp : nextGenSpecies) {
				if (nextGen[i].spicies == sp.representative) {
					fitness[i] = nextGen[i].getFitness()
							/ sp.individuals.size();
				}
			}

		}

		// display metrics
		Individual best1 = null;
		Individual best2 = null;
		Individual best3 = null;
		double avgFitness = 0;
		double avgTemplates = 0;
		double avgSD = 0;
		for (int i = 0; i < nextGen.length; i++) {
			if (best1 == null
					|| best1.getFitness() < nextGen[i].getFitness()) {
				best1 = nextGen[i];
			}
			avgFitness += nextGen[i].getFitness();
			avgTemplates += nextGen[i].getNumberOfTemplates();
			avgSD += nextGen[i].getStandardDeviationOfLogK();
		}
		avgFitness /= nextGen.length;
		avgTemplates /= nextGen.length;
		avgSD /= nextGen.length;
		for (int i = 0; i < nextGen.length; i++) {
			if (best2 == null
					|| best2.getFitness() < nextGen[i].getFitness()
					&& nextGen[i] != best1) {
				best2 = nextGen[i];
			}
		}
		for (int i = 0; i < nextGen.length; i++) {
			if (best3 == null
					|| best3.getFitness() < nextGen[i].getFitness()
					&& nextGen[i] != best1 && nextGen[i] != best2) {
				best3 = nextGen[i];
			}
		}

		speciesData[0][generation] = speciesVisualData.get(generation)
				.size();
		speciesData[1][generation] = Constants.speciationThreshold * 10;

		if(graphFrame.getComponentCount() != 1){
			System.err.println("Components are not working like I thought, or someone changed something");
		}
		
		panel.removeAll();
		panel1.removeAll();
		int nInspectSpecies = 5;
		double[][] newFitnessData = new double[nInspectSpecies * 2][speciesVisualData
				.size()];
		for (int i = 0; i < speciesVisualData.size(); i++) {
			ArrayList<Species> thisGeneSpecies = speciesVisualData.get(i);
			ArrayList<Species> bestSpeciesList = new ArrayList<Species>();
			for (int j = 0; j < nInspectSpecies; j++) {
				Species bestSpecies = null;
				for (Species sp : thisGeneSpecies) {
					if (!bestSpeciesList.contains(sp)) {
						if (bestSpecies == null
								|| bestSpecies.getBestIndividual()
										.getFitness() < sp
										.getBestIndividual().getFitness()) {
							bestSpecies = sp;
						}
					}
				}
				if (bestSpecies == null) {
					newFitnessData[j * 2][i] = 0;
					newFitnessData[j * 2 + 1][i] = 0;
				} else {
					newFitnessData[j * 2][i] = bestSpecies
							.getBestIndividual().getFitness();
					newFitnessData[j * 2 + 1][i] = bestSpecies
							.getTotalFitness()
							/ bestSpecies.individuals.size();
					bestSpeciesList.add(bestSpecies);
				}
			}
		}

		String[] names = new String[nInspectSpecies * 2];
		for (int i = 0; i < nInspectSpecies; i++) {
			names[i * 2] = "Best " + (i + 1);
			names[i * 2 + 1] = "Avg " + (i + 1);
		}


		if (!Constants.SILENT_MODE) {
			PlotSpeciesData demo4 = new PlotSpeciesData(speciesVisualData,
					graphFrame1.getSize().width, graphFrame1.getSize().height);

			PlotExpData demo = MyUtil.getGenericSpeciesFitnessPlot(speciesVisualData,
					demo4.getIndivColor());
			PlotExpData demo3 = new PlotExpData("Species over generation",
					speciesData, new String[] { "nSpecies",
							"Speciation Threshold" }, "Generation",
					"Number of species");
			//panel.add(demo);
			panel.add(demo3);
			panel1.add(demo4);
			graphFrame.setVisible(true);
			graphFrame1.setVisible(true);
		}
		((LoggerNEAT)logger).saveToFile(); //TODO: my work here is not done

		logger.logToMemory("Innovations:");
		for (String key : innovations.keySet()) {
			logger.logToMemory(key + ":" + innovations.get(key));
		}
		logger.logToMemory("Node names used:");
		for (String key : nodeNames.keySet()) {
			logger.logToMemory(key + ":" + nodeNames.get(key));
		}
		
	}

	protected void updateBestIndividuals(GraphDisplayer bestIndividuals,
			Individual individual) {
		bestIndividuals.addGraph(
				individual.getOligoSystem().getVisualization(250, 250),
				individual.displayTimeSereis(),
				individual.printModelInTextArea(), individual.parent1,
				individual.parent2);
		bestIndividuals.display();

	}

	public double compDistance(Individual indiv1, Individual indiv2) {
		CompatibilityDistance distance = CompatibilityDistance(indiv1, indiv2);
		return distance.distance();
	}

	public CompatibilityDistance CompatibilityDistance(Individual indiv1,
			Individual indiv2) {
		int nExcess = 0;
		int nDisjoint = 0;
		double avgWeightDifference = 0;
		int nMatches = 0;
		int N = Math.max(indiv1.getNEnabledConnections(),
				indiv2.getNEnabledConnections());
		for (int i = innovations.size(); i >= 1; i--) {
			Connection conn1 = indiv1.getConnection(i);
			Connection conn2 = indiv2.getConnection(i);
			if ((conn1 == null || !conn1.enabled)
					&& (conn2 == null || !conn2.enabled)) {
				continue;
			}
			if ((conn1 == null || !conn1.enabled)
					|| (conn2 == null || !conn2.enabled)) {
				if (nMatches == 0) {
					nExcess++;
				} else {
					nDisjoint++;
				}
			} else {
				nMatches++;
				avgWeightDifference += Math.abs(conn1.parameter
						/ conn1.from.parameter - conn2.parameter
						/ conn2.from.parameter);
			}
		}
		if (nMatches > 0) {
			avgWeightDifference = (double) avgWeightDifference / nMatches;
		}
		return new CompatibilityDistance(nExcess, nDisjoint,
				avgWeightDifference, N);
	}

	public Individual crossOver1Point(Individual indiv1, Individual indiv2)
	throws CloneNotSupportedException {
Individual tmp = getNewIndividual(indiv1.innovations, indiv1.nodeNames,
		-2, 0);
int genomeLength = innovations.size();
for (int i = innovations.size(); i >= 1; i--) {
	Connection conn1 = indiv1.getConnection(i);
	Connection conn2 = indiv2.getConnection(i);
	if (conn1 == null && conn2 == null) {
		genomeLength--;
	} else {
		break;
	}
}
if (genomeLength > 0) {
	int start = 1 + rand.nextInt(genomeLength);
	int end = genomeLength;
	for (int i = 1; i < start; i++) {
		Connection conn = indiv1.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
	for (int i = start; i < end; i++) {
		Connection conn = indiv2.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
	for (int i = end; i <= genomeLength; i++) {
		Connection conn = indiv1.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
} else {
	tmp = indiv1;
}
Individual child = getNewIndividual(this.innovations, this.nodeNames,
		-2, 0);
for (Connection connection : tmp.connectionGenes) {
	if (child.getNode(connection.from.name) == null) {
		child.nodeGenes.add(connection.from.clone());
	}
	if (child.getNode(connection.to.name) == null) {
		child.nodeGenes.add(connection.to.clone());
	}
	child.addConnection(child.getNode(connection.from.name),
			child.getNode(connection.to.name), connection.enabled,
			connection.parameter);
}

return child;
}
	
	public Individual crossOver2Points(Individual indiv1, Individual indiv2)
	throws CloneNotSupportedException {
Individual tmp = getNewIndividual(indiv1.innovations, indiv1.nodeNames,
		-2, 0);
int genomeLength = innovations.size();
for (int i = innovations.size(); i >= 1; i--) {
	Connection conn1 = indiv1.getConnection(i);
	Connection conn2 = indiv2.getConnection(i);
	if (conn1 == null && conn2 == null) {
		genomeLength--;
	} else {
		break;
	}
}
if (genomeLength > 0) {
	int start = 1 + rand.nextInt(genomeLength);
	int end = 1 + rand.nextInt(genomeLength);
	if (start > end) {
		int temp = start;
		start = end;
		end = temp;
	}
	for (int i = 1; i < start; i++) {
		Connection conn = indiv1.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
	for (int i = start; i < end; i++) {
		Connection conn = indiv2.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
	for (int i = end; i <= genomeLength; i++) {
		Connection conn = indiv1.getConnection(i);
		if (conn != null) {
			tmp.addConnection(conn.from, conn.to, conn.enabled,
					conn.parameter);
		}
	}
} else {
	tmp = indiv1;
}
Individual child = getNewIndividual(this.innovations, this.nodeNames,
		-2, 0);
for (Connection connection : tmp.connectionGenes) {
	if (child.getNode(connection.from.name) == null) {
		child.nodeGenes.add(connection.from.clone());
	}
	if (child.getNode(connection.to.name) == null) {
		child.nodeGenes.add(connection.to.clone());
	}
	child.addConnection(child.getNode(connection.from.name),
			child.getNode(connection.to.name), connection.enabled,
			connection.parameter);
}

return child;
}
	
	public Individual crossOver(Individual indiv1, Individual indiv2)
			throws CloneNotSupportedException {
		int nbSimpleSequences = (Integer) indiv1.annotations.get("nbSimpleSequences");
		HashMap<String,Integer> nti = new HashMap<String,Integer>(indiv1.nametoint);
		HashMap<Integer,String> itn = new HashMap<Integer,String>(indiv1.inttoname);
		for(Node node : indiv2.nodeGenes){
			if(node.type == Node.SIMPLE_SEQUENCE && indiv1.getNode(node.name) == null){
				nti.put(node.name, nbSimpleSequences);
				itn.put(nbSimpleSequences, node.name);
				nbSimpleSequences++;
			}
		}
		Individual tmp = getNewIndividual(nbSimpleSequences, indiv1.innovations, indiv1.nodeNames,
				-2, 0);
		tmp.nametoint = nti;
		tmp.inttoname = itn;
		for (int i = 1; i <= innovations.size(); i++) {
			Connection conn1 = indiv1.getConnection(i);
			Connection conn2 = indiv2.getConnection(i);
			if (conn1 != null && conn2 != null) {
				boolean enabled = true;
				if (!(conn1.enabled && conn2.enabled)
						&& rand.nextDouble() < Constants.probGeneDisabled) {
					enabled = false;
				}
				tmp.addConnection(conn1.from, conn1.to, enabled,
						rand.nextBoolean() ? conn1.parameter : conn2.parameter);
			} else if (conn1 != null
					&& indiv1.getFitness() >= indiv2.getFitness()) {
				tmp.addConnection(conn1.from, conn1.to, conn1.enabled,
						conn1.parameter);
			} else if (conn2 != null
					&& indiv2.getFitness() >= indiv1.getFitness()) {
				tmp.addConnection(conn2.from, conn2.to, conn2.enabled,
						conn2.parameter);
			}
		}
		Individual child = getNewIndividual(nbSimpleSequences, this.innovations, this.nodeNames,
				-2, 0);
		//child.nametoint = nti;
		//child.inttoname = itn;

		for (Connection connection : tmp.connectionGenes) {
			if (child.getNode(connection.from.name) == null) {
				child.nodeGenes.add(connection.from.clone());
			}
			if (child.getNode(connection.to.name) == null) {
				child.nodeGenes.add(connection.to.clone());
			}
			child.sanitizeNodes();
			child.addConnection(child.getNode(connection.from.name),
					child.getNode(connection.to.name), connection.enabled,
					connection.parameter);
		}
		return child;
	}

	private Individual[] selectCrossoverParents(ArrayList<Individual> curGen) {
		double maxFitness = -Double.MAX_VALUE;
		Individual parent1 = null;
		Individual parent2 = null;
		System.out.println(curGen.size());
		if (curGen.size() == 1) {
			return new Individual[] { curGen.get(0), curGen.get(0) };
		}

		for (int k = 0; k < Constants.tournamentSize; k++) {
			int winnerIndiv = this.rand.nextInt(curGen.size());
			Individual i = curGen.get(winnerIndiv);
			if (i.getFitness() > maxFitness) {
				parent1 = i;
				maxFitness = i.getFitness();
			}
		}
		maxFitness = -Double.MAX_VALUE;

		for (int k = 0; k < Constants.tournamentSize; k++) {
			Individual i = null;
			do {
				int winnerIndiv = this.rand.nextInt(curGen.size());
				i = curGen.get(winnerIndiv);
			} while (i == parent1);

			if (i.getFitness() > maxFitness) {
				parent2 = i;
				maxFitness = i.getFitness();
			}
		}

		return new Individual[] { parent1, parent2 };
	}

	protected Individual clone(Individual indiv)
			throws CloneNotSupportedException {
		Individual clone = getNewIndividual((Integer) indiv.annotations.get("nbSimpleSequences"), indiv.innovations, indiv.nodeNames,
				indiv.generationIndex, indiv.individualIndex);
		clone.genes = Array.copy(indiv.genes);
		clone.nametoint = new HashMap<String,Integer>(indiv.nametoint);
		clone.inttoname = new HashMap<Integer,String>(indiv.inttoname);
		if(clone.nametoint.size() != (Integer) indiv.annotations.get("nbSimpleSequences") || clone.inttoname.size() != (Integer) indiv.annotations.get("nbSimpleSequences")){
			System.out.println("Whaaaa");
		}
		for (Node node : indiv.nodeGenes) {
			clone.nodeGenes.add(node.clone());
		}
		for (Connection connection : indiv.connectionGenes) {
			clone.addConnection(clone.getNode(connection.from.name),
					clone.getNode(connection.to.name), connection.enabled,
					connection.parameter);
		}
		clone.exo = indiv.exo;
		clone.pol = indiv.pol;
		clone.nick = indiv.nick;
		clone.config = indiv.config;
		clone.parent1 = indiv.parent1;
		clone.parent2 = indiv.parent2;
		clone.mutationInfo = indiv.mutationInfo;
		return clone;
	}

}